#include <glib.h>
#include <sys/socket.h>
#include <sys/types.h>
//#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <string.h>

#include "config.h"
#include "sys_log.h"

#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <net/if_arp.h>

#define inaddrr(x) (*(struct in_addr *) &ifr->x[sizeof sa.sin_port])
#define IFRSIZE   ((int)(size * sizeof (struct ifreq)))

#define RECV_MAXSIZE 5*1024
static int connected_status = 0; // 0: disconnect 1:connected 

void (*DoExitApp)(void);
void (*DoAppRec)(char* buf, char* buf_len, int file_flag);

//int client_socket = 0;
typedef struct
{
	//connect socket
	int		destory_flag;
	GList *servers;
	//server info
	char *server_ip;
	int server_port;
	int server_socket_fd;
	
	void *client_player_t;

	//callback
	void (*client_cb)(void);
	void (*app_rec)(char* buf, long buf_len, int file_flag);
}controller_client_handler_s;

//volatile controller_client_handler_s* gClient = NULL; //multi-thread volatile
controller_client_handler_s* gClient = NULL;

void close_server_socketfd()
{
	int client_fd;

	if(NULL == gClient)	{
		SYS_LOG("close_server_socketfd return\n");
		return;
	}
	connected_status = 0;
	gClient->destory_flag = 1;

	close(gClient->server_socket_fd);

	SYS_LOG("!!!close_server_socketfd free gClient!!!\n");
	free(gClient->server_ip);
	gClient->server_ip = NULL;
	free(gClient);
	gClient = NULL;
}

static FILE * _recv_create_file()
{
	char* file_name = NULL;

	return	fopen(file_name, "w");	
}

static int _recv_to_file(char *buf,int buf_length, FILE* filefp)
{

	if (filefp == NULL)	
	{  
	   SYS_LOG("File:\t Can Not Open To Write!\n");  
	   exit(1);  
	}  
	int write_length = fwrite(buf, sizeof(char), buf_length, filefp);
	if (write_length < buf_length)	
	{  
		SYS_LOG("File:\t Write Failed!\n");  
		fclose(filefp);
		return -1;	
	}  

	return 1;
}

static void _client_recv_message(int client_fd)//when flag=1, buf is filepath
{
	SYS_LOG("_client_recv_message IN fd[%d] \n",client_fd);
	int recvbytes;

	int	  tmpbuflen = 0;
	int	  tmpfileflag = 0;
	char* filepath = NULL;
	int	  fileopen_flag = 0;
	FILE *fp = NULL;

	char* tmpbuf = (char*)malloc(RECV_MAXSIZE);
	while(1) {
		memset((void*)tmpbuf, 0, RECV_MAXSIZE);
		SYS_LOG("read to recv\n");
		if((recvbytes = recv(client_fd,tmpbuf,RECV_MAXSIZE,0)) == -1)	{
			SYS_LOG("recv error\n");
			free(tmpbuf);
			close_server_socketfd();
			return;
		}
		SYS_LOG("recvbytes[%d]\n",recvbytes);
		
		if(0 == recvbytes) {
			SYS_LOG("recv continue\n");
			close_server_socketfd();
			return;
		}

		if(recvbytes < RECV_MAXSIZE && 0 == tmpfileflag) {
			tmpbuflen = recvbytes;
			SYS_LOG("Server Message: [%s][%d]\n",tmpbuf,tmpbuflen);
			break;
		} else {
			if (0 == fileopen_flag){
				fp = _recv_create_file();
				fileopen_flag = 1;
			}
			if(-1 == _recv_to_file(tmpbuf,recvbytes-1,fp)) {SYS_LOG("save to file error\n"); return;}
			tmpfileflag = 1;
			tmpbuflen = tmpbuflen + recvbytes;
			if (recvbytes < RECV_MAXSIZE ) {
				SYS_LOG("rec finish\n");
				fclose(fp);
				break;
			}//finish
		}
	}
	
	if(NULL != gClient && NULL != gClient->app_rec) gClient->app_rec(tmpbuf,tmpbuflen,tmpfileflag);
	free(tmpbuf);
	tmpbuflen=tmpfileflag=0;
}

static void* _receive_socket(void *data)
{
	SYS_LOG("_receive_socket in\n");

	struct timeval tv;
	fd_set rfds;
	int retval, maxfd = -1;
	int recvbytes, server_socket = 0;
	
	if(NULL == gClient) {
		SYS_LOG("invalid parameter");
		return;
	}

	while(1)
	{
		FD_ZERO(&rfds);
		FD_SET(0,&rfds);
		maxfd = 0;
		if(gClient == NULL || 1 == gClient->destory_flag)
		{
			SYS_LOG("error 001 \n");
			if(gClient != NULL) gClient->client_cb();
			return;
		}
		while(0 == gClient->server_socket_fd) {//not connected
			sleep(10);
			continue;
		}
		FD_SET(gClient->server_socket_fd,&rfds);

		if(gClient->server_socket_fd > maxfd){
			maxfd = gClient->server_socket_fd;
		}
		tv.tv_sec = 2;
		tv.tv_usec = 0;
		retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
		if(retval == -1) {
			SYS_LOG("select error! \n");
			close_server_socketfd();
			break;
		}
		else if(retval == 0){
			sleep(10);
			continue;
		}
		else{
			if(FD_ISSET(gClient->server_socket_fd, &rfds))
			{//can recv data
				SYS_LOG("receive data! \n");
				_client_recv_message(gClient->server_socket_fd);
			}
		}
		
	}
	SYS_LOG("_receive_socket out");
}

void send_socket_message(int client_fd, char* send_buf, int send_buf_length)
{
	int sendbytes;
	if((sendbytes = send(client_fd, send_buf, send_buf_length,0)) == -1)
	{
		perror("send error ");
		exit(1);
	}
	SYS_LOG("client send success [%d]\n",sendbytes);
}

void send_socket_file(int client_fd, char* file_path)
{
	int sendbytes;

	SYS_LOG("client send success [%d]",sendbytes);
}

static void* _connect_server(void *data)
{

	struct sockaddr_in client_sockaddr;
	struct hostent *host;
	int client_socket;
	int try_count = 0;//try 3 times
	int pthreadStatus = 0;
	pthread_t pthreadId = 0;
	SYS_LOG("_connect_server IN \n");
	if(gClient == NULL)
	{
		return;
	}

	while (1) {
		if(gClient == NULL) break;
		if(connected_status == 1) { // it is connected.
			sleep(60);
			continue;
		}
	 	if((host = gethostbyname(gClient->server_ip)) == NULL)
	 	{
	 	 	SYS_LOG("gethostbyname error \n");
			sleep(5);
			continue;
	 	}

	 	if((client_socket = socket(AF_INET,SOCK_STREAM,0))==-1)
	 	{
	 		SYS_LOG("create socket error\n");
			//try_count++;
			sleep(5);
			continue;
	 	}
		SYS_LOG("client_fd = %d\n",client_socket);
	 	memset(&client_sockaddr,0,sizeof(client_sockaddr));
	 	client_sockaddr.sin_family = AF_INET;
	 	client_sockaddr.sin_port = htons(gClient->server_port);
	 	client_sockaddr.sin_addr = *((struct in_addr *)host->h_addr);
		bzero(&(client_sockaddr.sin_zero),8);
		SYS_LOG("reday to connect the server \n");

	 	if(connect(client_socket,(struct sockaddr *)&client_sockaddr,sizeof(struct sockaddr))==-1){
	  		SYS_LOG("connect error\n");
			close(client_socket);
			sleep(10);
			continue;
	 	}
		SYS_LOG("connecting OK \n");
		connected_status = 1;
		gClient->server_socket_fd = client_socket;
		continue;
	}
	SYS_LOG("_connect_server out\n");
}

void destory_socket_client()
{
	if (gClient == NULL) {
		SYS_LOG("_destory_ipc_client NULL");
		return;
	}
	close_server_socketfd();
	SYS_LOG("_destory_ipc_server out \n");
}

int init_socket_client(char* server_ip, int server_port, void* callback, void* rec_cb)
{
	int pthreadStatus = 0;
	pthread_t pthreadId = 0;

	SYS_LOG("_init_client_socket in[%s:%d]\n",server_ip, server_port);

	if(!server_ip) {
		SYS_LOG("invalid parameter");
		return -1;
	}
	if (gClient != NULL) {
		return gClient->server_socket_fd;
	}

	gClient = (controller_client_handler_s *)malloc(sizeof(controller_client_handler_s));
	memset((void*)gClient, 0, sizeof(controller_client_handler_s));

	gClient->server_ip = g_strdup(server_ip);
	gClient->server_port = server_port;
	SYS_LOG("gClient server info [%s:%d]\n", gClient->server_ip,gClient->server_port);

	//client_cb
	gClient->client_cb = callback;
	gClient->app_rec = rec_cb;
	pthreadStatus = pthread_create(&pthreadId, NULL, _connect_server, NULL);
	pthreadStatus = pthread_create(&pthreadId, NULL, _receive_socket, (void*)gClient);
	
	return gClient->server_socket_fd;
}

//http recv
int http_recv_data()
{

	struct sockaddr_in client_sockaddr;
	struct hostent *host;
	int client_socket;
	int try_count = 0;//try 3 times
	int pthreadStatus = 0;
	pthread_t pthreadId = 0;
	SYS_LOG("_connect_server IN \n");
	if(gClient == NULL)
	{
		return;
	}


 	if((host = gethostbyname(gClient->server_ip)) == NULL){
		return -1;
 	}

 	if((client_socket = socket(AF_INET,SOCK_STREAM,0))==-1)
 	{
 		SYS_LOG("create socket error\n");
		return -1;
 	}
	SYS_LOG("client_fd = %d\n",client_socket);
 	memset(&client_sockaddr,0,sizeof(client_sockaddr));
 	client_sockaddr.sin_family = AF_INET;
 	client_sockaddr.sin_port = htons(gClient->server_port);
 	client_sockaddr.sin_addr = *((struct in_addr *)host->h_addr);
	bzero(&(client_sockaddr.sin_zero),8);
	SYS_LOG("reday to connect the server \n");

 	if(connect(client_socket,(struct sockaddr *)&client_sockaddr,sizeof(struct sockaddr))==-1){
  		SYS_LOG("connect error\n");
		close(client_socket);
		return -1;
 	}
	SYS_LOG("connecting OK \n");

	//send get method	
	//send_socket_message(client_socket, char* send_buf, int send_buf_length);

	//recv data
	struct timeval tv;
	fd_set rfds;
	int retval, maxfd = -1;
	int recvbytes, server_socket = 0;
	
	while(1)
	{
		FD_ZERO(&rfds);
		FD_SET(0,&rfds);
		maxfd = 0;
		if(gClient == NULL || 1 == gClient->destory_flag)
		{
			close(client_socket);
			return -1;
		}
		FD_SET(client_socket,&rfds);
		if(client_socket > maxfd){
			maxfd = client_socket;
		}
		tv.tv_sec = 2;
		tv.tv_usec = 0;
		retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
		if(retval == -1) {
			SYS_LOG("select error! \n");
			close(client_socket);
			break;
		}
		else if(retval == 0){
			sleep(10);
			continue;
		}
		else{
			if(FD_ISSET(client_socket, &rfds))
			{//can recv http  data
				SYS_LOG("receive data! \n");
				_client_recv_message(client_socket);
			}
		}
		
	}
	close(client_socket);
	SYS_LOG("_receive_socket out");
}



static long tcp_heart_beat_data(void *data, int size, int nmemb, void* usr_data)
{
	SYS_LOG( "heart_beat response recv\n");
	return 1;
}

gboolean tcp_heart_beat_post(char* buf)
{
	if(NULL != gClient && 0 == gClient->server_socket_fd) {
		send_socket_message(gClient->server_socket_fd,buf,sizeof(buf));
		return TRUE;
	}
    return FALSE;
}

#if 0
int get_ip_mac_address(char** ip_address, char** mac_address)
{
	  unsigned char      *u;
	  int                sockfd, size  = 1;
	  char pMacBuffer[20] = {0};
	  struct ifreq       *ifr;
	  struct ifconf      ifc;
	  struct sockaddr_in sa;

	  if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) {
		SYS_LOG("Cannot open socket.");
		return -1;
	  }

	  ifc.ifc_len = IFRSIZE;
	  ifc.ifc_req = NULL;

	  do {
		++size;
		/* realloc buffer size until no overflow occurs  */
		if (NULL == (ifc.ifc_req = (struct ifreq *)realloc(ifc.ifc_req, IFRSIZE))) {
			SYS_LOG("Out of memory.");
			return -1;
		}
		ifc.ifc_len = IFRSIZE;
		if (ioctl(sockfd, SIOCGIFCONF, &ifc)) {
			SYS_LOG("ioctl SIOCFIFCONF.");
			free(ifc.ifc_req);
			return -1;
		}
	  } while  (IFRSIZE <= ifc.ifc_len);
	  
		int iCnt = 0;
		ifr = ifc.ifc_req;
	  for (;(char *) ifr < (char *) ifc.ifc_req + ifc.ifc_len; ++ifr) {

		if (ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data) {
			SYS_LOG("duplicate one, skip");
		  continue;  /* duplicate, skip it */
		}

		if (ioctl(sockfd, SIOCGIFFLAGS, ifr)) {
			SYS_LOG("failed to get flags, skip");
		  continue;  /* failed to get flags, skip it */
		}

		char *strMediaAddress = inet_ntoa(inaddrr(ifr_addr.sa_data));

		if (0 == ioctl(sockfd, SIOCGIFHWADDR, ifr)) {
		  switch (ifr->ifr_hwaddr.sa_family) {
			  default:
                 //SYS_LOG("Unknown ifr->ifr_hwaddr.sa_family =%d\n", ifr->ifr_hwaddr.sa_family);
				 break;
			  case  ARPHRD_NETROM:  case  ARPHRD_ETHER:  case  ARPHRD_PPP:
			  case  ARPHRD_EETHER:  case  ARPHRD_IEEE802:
				{
					if(strncmp(strMediaAddress, "127.", 4))
					{
						u = ( unsigned char * )&ifr->ifr_addr.sa_data;

						if ( u[0] + u[1] + u[2] + u[3] + u[4] + u[5] )
						{
							sprintf(pMacBuffer,  "%2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x",
									u[0], u[1], u[2], u[3], u[4], u[5] );
							*mac_address = g_strdup(pMacBuffer);
							*ip_address = g_strdup(strMediaAddress);
							
							break;
						}
						else
						{
							SYS_LOG("No HW address, u[0]=%d, u[1]=%d\n", u[0], u[1]);
						}
					}
					break;
				}
		  }
		}
		else
		{
			SYS_LOG("ioctl(sockfd, SIOCGIFHWADDR, ifr) fail!\n");
		}
	  }
	  free(ifc.ifc_req);
	  close(sockfd);
	  return 1;
}
#else
int get_ip_mac_address(char** ip_address, char** mac_address)
{
    int sockfd,i;
    struct ifreq tmp;   
    char mac_addr[30];

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if( sockfd < 0)
    {
        perror("create socket fail\n");
        return -1;
    }

    memset(&tmp,0,sizeof(struct ifreq));
    for(i=0;i<99;i++) {
    	sprintf(tmp.ifr_name,"wlan%d",i);
    	if( (ioctl(sockfd,SIOCGIFHWADDR,&tmp)) < 0 )
    	{
        	printf("mac wlan error, next\n");
        	continue;
    	}
        sprintf(mac_addr, "%02x-%02x-%02x-%02x-%02x-%02x",
            (unsigned char)tmp.ifr_hwaddr.sa_data[0],
            (unsigned char)tmp.ifr_hwaddr.sa_data[1],
            (unsigned char)tmp.ifr_hwaddr.sa_data[2],
            (unsigned char)tmp.ifr_hwaddr.sa_data[3],
            (unsigned char)tmp.ifr_hwaddr.sa_data[4],
            (unsigned char)tmp.ifr_hwaddr.sa_data[5]
            );
        *ip_address = g_strdup(mac_addr);
        *mac_address = g_strdup(mac_addr);
        close(sockfd);
        return 0;
    }
    close(sockfd);
/*
    strncpy(tmp.ifr_name,"eth2",sizeof(tmp.ifr_name)-1);
    if( (ioctl(sockfd,SIOCGIFHWADDR,&tmp)) < 0 )
    {
        printf("mac ioctl error\n");
        return -1;
    }

    sprintf(mac_addr, "%02x-%02x-%02x-%02x-%02x-%02x",
            (unsigned char)tmp.ifr_hwaddr.sa_data[0],
            (unsigned char)tmp.ifr_hwaddr.sa_data[1],
            (unsigned char)tmp.ifr_hwaddr.sa_data[2],
            (unsigned char)tmp.ifr_hwaddr.sa_data[3],
            (unsigned char)tmp.ifr_hwaddr.sa_data[4],
            (unsigned char)tmp.ifr_hwaddr.sa_data[5]
            );
    printf("local eth0 mac:%s\n", mac_addr);
    close(sockfd);
    *ip_address = g_strdup(mac_addr);
    
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if( sockfd < 0)
    {
        perror("create socket fail\n");
        return -1;
    }

    memset(&tmp,0,sizeof(struct ifreq));
    strncpy(tmp.ifr_name,"wlan0",sizeof(tmp.ifr_name)-1);
    if( (ioctl(sockfd,SIOCGIFHWADDR,&tmp)) < 0 )
    {
        printf("mac ioctl error\n");
        return -1;
    }

    sprintf(mac_addr, "%02x-%02x-%02x-%02x-%02x-%02x",
            (unsigned char)tmp.ifr_hwaddr.sa_data[0],
            (unsigned char)tmp.ifr_hwaddr.sa_data[1],
            (unsigned char)tmp.ifr_hwaddr.sa_data[2],
            (unsigned char)tmp.ifr_hwaddr.sa_data[3],
            (unsigned char)tmp.ifr_hwaddr.sa_data[4],
            (unsigned char)tmp.ifr_hwaddr.sa_data[5]
            );
    printf("local wlan mac:%s\n", mac_addr);
    close(sockfd);
    *mac_address = g_strdup(mac_addr);
*/
    return 0;
}


#endif

///for test
#if 0
static void do_exit_app ()
{
	SYS_LOG("do_exit_app\n");
}

static void do_recv_app(char* buf, char* buf_len, int file_flag)
{
	SYS_LOG("do_recv_app\n");
}

static void interpret (char *cmd)
{
	if (strncmp(cmd, "i", 1) == 0)
    {
		init_socket_client("127.0.0.1", 9501,do_exit_app,do_recv_app);
    }
	else if (strncmp(cmd, "s", 1) == 0)
    {
		send_socket_message(gClient->server_socket_fd,"Hello",5);
    }

}

gboolean input (GIOChannel *channel)
{
    char buf[5 + 3];
    gsize read;

    g_io_channel_read(channel, buf, 5, &read);
    buf[read] = '\0';
	g_strstrip(buf);
    interpret (buf);
    return TRUE;
}

GMainLoop *g_loop;

int main(int argc, char *argv[])
{
	int ret = 0;
	GIOChannel *stdin_channel;

	stdin_channel = g_io_channel_unix_new(0);
	g_io_add_watch(stdin_channel, G_IO_IN, (GIOFunc)input, NULL);
	SYS_LOG("Please input cmd\n");
	SYS_LOG("i: start server\n");
	SYS_LOG("s: send message to server\n");

	SYS_LOG (" <<<< GTK Loop >>>>");
	g_loop = g_main_loop_new(NULL, FALSE);
	g_main_loop_run(g_loop);
    SYS_LOG("exit main\n");
	return 0;
}
#endif
